#pragma once

#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>

#include "vmap/bih.h"

namespace vmap
{
    class TreeNode;
    struct AreaInfo;
    struct LocationInfo;

    /**
     * @brief
     *
     */
    class MeshTriangle
    {
    public:
        /**
         * @brief
         *
         */
        MeshTriangle() {};
        /**
         * @brief
         *
         * @param na
         * @param nb
         * @param nc
         */
        MeshTriangle(unsigned int na, unsigned int nb, unsigned int nc) : idx0(na), idx1(nb), idx2(nc) {};

        unsigned int idx0; /**< TODO */
        unsigned int idx1; /**< TODO */
        unsigned int idx2; /**< TODO */
    };

    /**
     * @brief
     *
     */
    class WmoLiquid
    {
    public:
        /**
         * @brief
         *
         * @param width
         * @param height
         * @param corner
         * @param type
         */
        WmoLiquid(unsigned int width, unsigned int height, const Vector3& corner, unsigned int type);
        /**
         * @brief
         *
         * @param other
         */
        WmoLiquid(const WmoLiquid& other);
        /**
         * @brief
         *
         */
        ~WmoLiquid();
        /**
         * @brief
         *
         * @param other
         * @return WmoLiquid &operator
         */
        WmoLiquid& operator=(const WmoLiquid& other);
        /**
         * @brief
         *
         * @param pos
         * @param liqHeight
         * @return bool
         */
        bool GetLiquidHeight(const Vector3& pos, float& liqHeight) const;
        /**
         * @brief
         *
         * @return unsigned int
         */
        unsigned int GetType() const { return iType; }
        /**
         * @brief
         *
         * @return float
         */
        float* GetHeightStorage() { return iHeight; }
        /**
         * @brief
         *
         * @return unsigned char
         */
        unsigned char* GetFlagsStorage() { return iFlags; }
        /**
         * @brief
         *
         * @return unsigned int
         */
        unsigned int GetFileSize();
        /**
         * @brief
         *
         * @param wf
         * @return bool
         */
        bool WriteToFile(FILE* wf);
        /**
         * @brief
         *
         * @param rf
         * @param liquid
         * @return bool
         */
        static bool ReadFromFile(FILE* rf, WmoLiquid*& liquid);
    private:
        /**
         * @brief
         *
         */
        WmoLiquid() : iHeight(0), iFlags(0) {};

        unsigned int iTilesX;  /**< number of tiles in x direction, each */
        unsigned int iTilesY;  /**< TODO */
        Vector3 iCorner; /**< the lower corner */
        unsigned int iType;    /**< liquid type */
        float* iHeight;  /**< (tilesX + 1)*(tilesY + 1) height values */
        unsigned char* iFlags;   /**< info if liquid tile is used */
#ifdef MMAP_GENERATOR
    public:
        void getPosInfo(unsigned int& tilesX, unsigned int& tilesY, Vector3& corner) const;
#endif
    };

    /**
     * @brief holding additional info for WMO group files
     *
     */
    class GroupModel
    {
    public:
        /**
         * @brief
         *
         */
        GroupModel() : iLiquid(0) {}
        /**
         * @brief
         *
         * @param other
         */
        GroupModel(const GroupModel& other);
        /**
         * @brief
         *
         * @param mogpFlags
         * @param groupWMOID
         * @param bound
         */
        GroupModel(unsigned int mogpFlags, unsigned int groupWMOID, const AABox& bound) :
            iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(0) {
        }
        /**
         * @brief
         *
         */
        ~GroupModel() { delete iLiquid; }

        /**
         * @brief pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
         *
         * @param vert
         * @param tri
         */
        void SetMeshData(std::vector<Vector3>& vert, std::vector<MeshTriangle>& tri);
        /**
         * @brief
         *
         * @param liquid
         */
        void SetLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = NULL; }
        /**
         * @brief
         *
         * @param ray
         * @param distance
         * @param stopAtFirstHit
         * @return bool
         */
        bool IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit) const;
        /**
         * @brief
         *
         * @param pos
         * @param down
         * @param z_dist
         * @return bool
         */
        bool IsInsideObject(const Vector3& pos, const Vector3& down, float& z_dist) const;
        /**
         * @brief
         *
         * @param pos
         * @param liqHeight
         * @return bool
         */
        bool GetLiquidLevel(const Vector3& pos, float& liqHeight) const;
        /**
         * @brief
         *
         * @return unsigned int
         */
        unsigned int GetLiquidType() const;
        /**
         * @brief
         *
         * @param wf
         * @return bool
         */
        bool WriteToFile(FILE* wf);
        /**
         * @brief
         *
         * @param rf
         * @return bool
         */
        bool ReadFromFile(FILE* rf);
        /**
         * @brief
         *
         * @return const G3D::AABox
         */
        const G3D::AABox& GetBound() const { return iBound; }
        /**
         * @brief
         *
         * @return unsigned int
         */
        unsigned int GetMogpFlags() const { return iMogpFlags; }
        /**
         * @brief
         *
         * @return unsigned int
         */
        unsigned int GetWmoID() const { return iGroupWMOID; }
    protected:
        G3D::AABox iBound;  /**< TODO */
        unsigned int iMogpFlags;  /**< 0x8 outdor; 0x2000 indoor */
        unsigned int iGroupWMOID; /**< TODO */
        std::vector<Vector3> vertices; /**< TODO */
        std::vector<MeshTriangle> triangles; /**< TODO */
        BIH meshTree; /**< TODO */
        WmoLiquid* iLiquid; /**< TODO */

#ifdef MMAP_GENERATOR
    public:
        void getMeshData(std::vector<Vector3>& vertices, std::vector<MeshTriangle>& triangles, WmoLiquid*& liquid);
#endif
    };
    /**
     * @brief Holds a model (converted M2 or WMO) in its original coordinate space
     *
     */
    class WorldModel
    {
    public:
        /**
         * @brief
         *
         */
        WorldModel() : RootWMOID(0) {}

        /**
         * @brief pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
         *
         * @param models
         */
        void SetGroupModels(std::vector<GroupModel>& models);
        /**
         * @brief
         *
         * @param id
         */
        void SetRootWmoID(unsigned int id) { RootWMOID = id; }
        /**
         * @brief
         *
         * @param ray
         * @param distance
         * @param stopAtFirstHit
         * @return bool
         */
        bool IntersectRay(const G3D::Ray& ray, float& distance, bool stopAtFirstHit) const;
        /**
         * @brief
         *
         * @param point
         * @param dir
         * @param dist
         * @param info
         * @return bool
         */
        bool GetAreaInfo(const G3D::Vector3& point, const G3D::Vector3& dir, float& dist, AreaInfo& info) const;
        /**
         * @brief
         *
         * @param point
         * @param dir
         * @param dist
         * @param info
         * @return bool
         */
        bool GetLocationInfo(const G3D::Vector3& point, const G3D::Vector3& dir, float& dist, LocationInfo& info) const;
        /**
         * @brief
         *
         * @param point
         * @param dir
         * @param dist
         * @return bool
         */
        bool GetContactPoint(const G3D::Vector3& point, const G3D::Vector3& dir, float& dist) const;
        /**
         * @brief
         *
         * @param filename
         * @return bool
         */
        bool WriteFile(const std::string& filename);
        /**
         * @brief
         *
         * @param filename
         * @return bool
         */
        bool ReadFile(const std::string& filename);
        unsigned int Flags;
    protected:
        unsigned int RootWMOID; /**< TODO */
        std::vector<GroupModel> groupModels; /**< TODO */
        BIH groupTree; /**< TODO */

#ifdef MMAP_GENERATOR
    public:
        void getGroupModels(std::vector<GroupModel>& groupModels);
#endif
    };

}
